package trading_strategy.strategies;

import org.apache.log4j.Logger;
import trading_strategy.Strategy;
import trading_strategy.events.AbstractMarketEvent;
import trading_strategy.events.EventManager;
import trading_strategy.events.MarketEvent;
import trading_strategy.events.OrderBookUpdate;
import trading_strategy.events.filtered.Candle;
import trading_strategy.events.filtered.CandleFilter;
import trading_strategy.events.filtered.ImprovedCandleFilter;
import trading_strategy.instruments.ListedInstrument;
import trading_strategy.orders.Order;
import trading_strategy.orders.SingleOrderAutomaton;
import trading_strategy.positions.InstrumentPosition;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: gderoujoux
 * Date: 24 mai 2010
 * Time: 16:26:16
 * To change this template use File | Settings | File Templates.
 */
public class ClusteringDataGenerator extends Strategy implements SingleOrderAutomaton.OrderProvider, TriggerPositionEnterer.PositionEntererListener {
    class CandleWithLastBidAsk {
        public CandleWithLastBidAsk(Candle candle) {
            this.candle = candle;
        }

        public double getOpen() {
            return candle.getOpen();
        }

        public double getHigh() {
            return candle.getHigh();
        }

        public double getLow() {
            return candle.getLow();
        }

        public double getClose() {
            return candle.getClose();
        }

        public double getVwap() {
            return candle.getVwap();
        }

        public int getTradedQty() {
            return candle.getTradedQty();
        }

        public Date getStart() {
            return candle.getStart();
        }

        public Date getEnd() {
            return candle.getEnd();
        }

        public double getVolume() {
            return candle.getVolume();
        }

        public double getBid() {
            return bid;
        }

        public double getAsk() {
            return ask;
        }

        public int getBidQty() {
            return bidQty;
        }

        public int getAskQty() {
            return askQty;
        }

        public int getBidTradedQty() {
            return candle.getBidTradedQty();
        }

        public int getAskTradedQty() {
            return candle.getAskTradedQty();
        }

        Candle candle;
        double bid;
        double ask;
        int bidQty;
        int askQty;
    }
    public static final Logger LOGGER = Logger.getLogger(ClusteringDataGenerator.class);
    SingleOrderAutomaton automaton;
    private Date date = this.date;

    private InstrumentPosition position;
    private static final char CSV_SEPARATOR = ',';
    private static final int NB_CANDLES_PER_INTERVAL = 5;

    ListedInstrument instrument;
    ArrayList<CandleWithLastBidAsk> lastCandles = new ArrayList<CandleWithLastBidAsk>();
    OrderBookUpdate lastOrderBook = null;
    FileWriter candleWriter;

    @Override
    public double getPnL() {
        return position.getPnL();
    }

    public ClusteringDataGenerator(Date date, ListedInstrument instrument) {
        this.date = date;
        this.instrument = instrument;
        this.automaton = new SingleOrderAutomaton(instrument, this);
        this.position = new InstrumentPosition(instrument);
        try {
            File file = new File("clustering.csv");
            boolean writeHeader = !file.exists();
            this.candleWriter = new FileWriter(file, true);
            if (writeHeader) {
                for (int i = 0; i < NB_CANDLES_PER_INTERVAL; i++) {
                    candleWriter.write("Return_" + (NB_CANDLES_PER_INTERVAL - i));
                    candleWriter.write(CSV_SEPARATOR);
                    candleWriter.write("Amplitude_" + (NB_CANDLES_PER_INTERVAL - i));
                    candleWriter.write(CSV_SEPARATOR);
                    candleWriter.write("VWAP_" + (NB_CANDLES_PER_INTERVAL - i));
                    candleWriter.write(CSV_SEPARATOR);
                    candleWriter.write("VOL_" + (NB_CANDLES_PER_INTERVAL - i));
                    candleWriter.write(CSV_SEPARATOR);
                    candleWriter.write("Spread_" + (NB_CANDLES_PER_INTERVAL - i));
                    candleWriter.write(CSV_SEPARATOR);
                    candleWriter.write("VolumeImbalance_" + (NB_CANDLES_PER_INTERVAL - i));
                    candleWriter.write(CSV_SEPARATOR);
                    candleWriter.write("BidTraded_" + (NB_CANDLES_PER_INTERVAL - i));
                    candleWriter.write(CSV_SEPARATOR);
                    candleWriter.write("AskTraded_" + (NB_CANDLES_PER_INTERVAL - i));
                    candleWriter.write(CSV_SEPARATOR);

                }
                candleWriter.write("High_return");
                candleWriter.write(CSV_SEPARATOR);
                candleWriter.write("Low_return");
                candleWriter.write(CSV_SEPARATOR);
                candleWriter.write("Greater_return");
                candleWriter.write(CSV_SEPARATOR);
                candleWriter.write("Close_return");
                candleWriter.write('\n');
            }
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }

    @Override
    public void onMarketEvent(AbstractMarketEvent event) {
        if (event instanceof MarketEvent) {
            if (((MarketEvent) event).getState() == MarketEvent.MarketState.CLOSED) {
                stop();
            }
            if (((MarketEvent) event).getState() == MarketEvent.MarketState.OPEN) {
                start();
            }
        }
        if (!isStarted()) {
            return;
        }
        if (event instanceof OrderBookUpdate) {
            lastOrderBook = (OrderBookUpdate) event;
        } else if (event instanceof CandleFilter.CandleEvent) {
            CandleFilter.CandleEvent candleEvent = (CandleFilter.CandleEvent) event;
            CandleWithLastBidAsk cwba = new CandleWithLastBidAsk(candleEvent.getCandle());
            cwba.ask = lastOrderBook.getBook().getAsk(0);
            cwba.bid = lastOrderBook.getBook().getBid(0);
            cwba.askQty = lastOrderBook.getBook().getAskQty(0);
            cwba.bidQty = lastOrderBook.getBook().getBidQty(0);

            lastCandles.add(cwba);
            int size = lastCandles.size();
            if (size > NB_CANDLES_PER_INTERVAL + 1) {
                // Dump one line
                CandleWithLastBidAsk lastCandle = lastCandles.get(lastCandles.size() - 1);
                List<CandleWithLastBidAsk> prevCandles = this.lastCandles.subList(size - NB_CANDLES_PER_INTERVAL - 1, size - 1);
                CandleWithLastBidAsk prevCandle = prevCandles.get(prevCandles.size() - 2);
                try {
                    for (CandleWithLastBidAsk candle : prevCandles) {
                        candleWriter.write(Double.toString(prevCandle.getClose() - candle.getOpen()));
                        candleWriter.write(CSV_SEPARATOR);
                        candleWriter.write(Double.toString(candle.getHigh() - candle.getLow()));
                        candleWriter.write(CSV_SEPARATOR);
                        candleWriter.write(Double.toString(candle.getVwap()));
                        candleWriter.write(CSV_SEPARATOR);
                        candleWriter.write(Double.toString(candle.getVolume()));
                        candleWriter.write(CSV_SEPARATOR);
                        candleWriter.write(Double.toString(candle.getAsk() - candle.getBid()));
                        candleWriter.write(CSV_SEPARATOR);
                        candleWriter.write(Double.toString(candle.getAskQty() - candle.getBidQty()));
                        candleWriter.write(CSV_SEPARATOR);
                        candleWriter.write(Integer.toString(candle.getBidTradedQty()));
                        candleWriter.write(CSV_SEPARATOR);
                        candleWriter.write(Integer.toString(candle.getAskTradedQty()));
                        candleWriter.write(CSV_SEPARATOR);
                    }
                    // Values to predict
                    double maxGain = lastCandle.getHigh() - prevCandle.getClose();
                    candleWriter.write(Double.toString(maxGain));
                    candleWriter.write(CSV_SEPARATOR);
                    double maxLoss = lastCandle.getLow() - prevCandle.getClose();
                    candleWriter.write(Double.toString(maxLoss));
                    candleWriter.write(CSV_SEPARATOR);
                    candleWriter.write(Double.toString(Math.abs(maxLoss) > Math.abs(maxGain) ? maxLoss : maxGain));
                    candleWriter.write(CSV_SEPARATOR);
                    candleWriter.write(Double.toString(lastCandle.getClose() - prevCandle.getClose()));
                    candleWriter.write('\n');
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public boolean shouldStillSendOrder(Order order) {
        return true;
    }

    @Override
    public void onExecution(int qty, double price, Order order) {
    }

    public void register() {
        // Subscribe to events classes
        CandleFilter cf = new ImprovedCandleFilter(instrument, "candles", 30);
        cf.subscribe();
        EventManager.getInstance().subscribe(this, instrument.getMarket().getMarketEventProvider());
        EventManager.getInstance().subscribe(this, instrument.getOrderBookProvider());
        EventManager.getInstance().subscribe(this, cf);
    }

    @Override
    public void deactivated() {
    }

    @Override
    public void stop() {
        super.stop();
        try {
            candleWriter.close();
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }
}